import binascii
import datetime
import os
import struct
import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox

import ttkbootstrap as ttkb
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


class AddressInputDialog(tk.Toplevel):
    """地址输入对话框"""

    def __init__(self, parent, title=None):
        super().__init__(parent)
        self.parent = parent
        self.result = None

        if title:
            self.title(title)

        self.geometry("300x120")
        self.resizable(False, False)

        # 确保对话框是模态的
        self.transient(parent)
        self.grab_set()

        # 创建界面
        frame = ttkb.Frame(self, padding=15)
        frame.pack(fill=BOTH, expand=True)

        ttkb.Label(frame, text="请输入起始地址 (十六进制):").pack(anchor=W, pady=(0, 5))

        self.addr_var = ttkb.StringVar(value="00000000")
        addr_entry = ttkb.Entry(frame, textvariable=self.addr_var, font=("Consolas", 12))
        addr_entry.pack(fill=X, pady=(0, 10))
        addr_entry.focus_set()

        # 按钮区域
        btn_frame = ttkb.Frame(frame)
        btn_frame.pack(fill=X)

        ok_btn = ttkb.Button(btn_frame, text="确定", command=self.on_ok, bootstyle=SUCCESS)
        ok_btn.pack(side=LEFT, padx=(0, 5), fill=X, expand=True)

        cancel_btn = ttkb.Button(btn_frame, text="取消", command=self.on_cancel, bootstyle=DANGER)
        cancel_btn.pack(side=LEFT, padx=(5, 0), fill=X, expand=True)

        # 居中显示
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.parent.winfo_width() // 2) - (width // 2) + self.parent.winfo_x()
        y = (self.parent.winfo_height() // 2) - (height // 2) + self.parent.winfo_y()
        self.geometry(f"+{x}+{y}")

    def on_ok(self):
        self.result = self.addr_var.get().strip()
        self.destroy()

    def on_cancel(self):
        self.destroy()


@annotate(ClassInfo(name="AbinGenerator", category="dev_tool"))
class AbinView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 数据存储
        self.file_entries = []  # 存储多个文件信息: [{path, addr, size, data, type, name, status}, ...]
        self.log_messages = []
        self.firmware_data = b""  # 合并后的固件数据
        self.address_ranges = []  # 地址范围列表: [(start_addr, end_addr), ...]

        # TLV类型定义 (4字节Type)
        self.TLV_AUTHOR = 0x00000001
        self.TLV_TIMESTAMP = 0x00000002
        self.TLV_HW_VERSION = 0x00000003
        self.TLV_DEVICE_NAME = 0x00000004
        self.TLV_DEVICE_CODE = 0x00000005
        self.TLV_FILE_COUNT = 0x00000006  # 文件总数
        self.TLV_FILE_INFO = 0x00000007  # 单个文件信息
        self.TLV_FILE_DATA = 0x00000008  # 文件数据
        self.TLV_CHUNK_INDEX = 0x00000009  # 数据块索引
        self.TLV_FILE_CHECKSUM = 0x0000000A  # 文件校验和

        # 固件分段大小 (64KB)
        self.CHUNK_SIZE = 65536

        # 创建界面
        self._create_widgets()

        # 初始化日志
        self.log("固件生成工具启动成功")
        self.log("支持多文件添加，每个文件可指定独立地址并自动检测地址重叠")

    def _create_widgets(self):
        # 主框架
        main_frame = ttkb.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=YES)

        # 顶部标题
        title_label = ttkb.Label(main_frame, text="abin固件生成工具", font=("微软雅黑", 16, "bold"))
        title_label.pack(pady=3)

        # 主要内容区域 (左右布局)
        content_frame = ttkb.Frame(main_frame)
        content_frame.pack(fill=BOTH, expand=YES, pady=5)

        # 左侧：文件选择和参数输入（占2/3宽度）
        left_frame = ttkb.LabelFrame(content_frame, text="文件与参数设置", padding=10)
        left_frame.pack(side=LEFT, fill=BOTH, expand=YES, padx=5)

        # 多文件管理区域
        files_frame = ttkb.LabelFrame(left_frame, text="固件文件列表", padding=10)
        files_frame.pack(fill=BOTH, expand=YES, pady=5)

        # 文件列表控制按钮
        file_controls_frame = ttkb.Frame(files_frame)
        file_controls_frame.pack(fill=X, pady=5)

        add_btn = ttkb.Button(file_controls_frame, text="添加文件", command=self.add_file, bootstyle=SUCCESS)
        add_btn.pack(side=LEFT, padx=5)

        remove_btn = ttkb.Button(file_controls_frame, text="移除选中", command=self.remove_file, bootstyle=DANGER)
        remove_btn.pack(side=LEFT, padx=5)

        clear_btn = ttkb.Button(file_controls_frame, text="清空列表", command=self.clear_files, bootstyle=WARNING)
        clear_btn.pack(side=LEFT, padx=5)

        check_btn = ttkb.Button(file_controls_frame, text="检查地址重叠", command=self.check_all_overlaps,
                                bootstyle=INFO)
        check_btn.pack(side=LEFT, padx=5)

        # 文件列表表格
        columns = ("文件名", "类型", "起始地址(十六进制)", "大小(字节)", "状态")
        self.file_tree = ttkb.Treeview(files_frame, columns=columns, show="headings")

        # 设置列宽和标题
        for col in columns:
            self.file_tree.heading(col, text=col)
            width = 150 if col != "文件名" else 250
            self.file_tree.column(col, width=width, anchor=tk.CENTER)

        # 添加滚动条
        scrollbar = ttkb.Scrollbar(files_frame, orient=tk.VERTICAL, command=self.file_tree.yview)
        self.file_tree.configure(yscroll=scrollbar.set)

        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.file_tree.pack(fill=BOTH, expand=YES, pady=5)

        # 操作按钮区域
        operations_frame = ttkb.Frame(left_frame)
        operations_frame.pack(fill=X, pady=10)

        generate_btn = ttkb.Button(operations_frame, text="生成ABIN固件", command=self.generate_abin, bootstyle=SUCCESS)
        generate_btn.pack(side=LEFT, padx=5, fill=X, expand=YES)

        read_abin_btn = ttkb.Button(operations_frame, text="加载ABIN文件", command=self.read_abin, bootstyle=INFO)
        read_abin_btn.pack(side=LEFT, padx=5, fill=X, expand=YES)

        export_hex_btn = ttkb.Button(operations_frame, text="导出为HEX", command=self.export_hex, bootstyle=PRIMARY)
        export_hex_btn.pack(side=LEFT, padx=5, fill=X, expand=YES)

        export_bin_btn = ttkb.Button(operations_frame, text="导出为BIN", command=self.export_bin, bootstyle=PRIMARY)
        export_bin_btn.pack(side=LEFT, padx=5, fill=X, expand=YES)

        # 设备信息区域
        device_info_frame = ttkb.LabelFrame(left_frame, text="设备信息", padding=10)
        device_info_frame.pack(fill=X, pady=5)

        # 设备名称和硬件版本（同一行，设备名称在前）
        device_hw_frame = ttkb.Frame(device_info_frame)
        device_hw_frame.pack(fill=X, pady=5)

        # 设备名称部分
        device_name_frame = ttkb.Frame(device_hw_frame)
        device_name_frame.pack(side=LEFT, fill=X, expand=YES, padx=(0, 10))
        ttkb.Label(device_name_frame, text="设备名称:").pack(side=LEFT, padx=5)
        self.device_name_var = ttkb.StringVar(value="")
        device_name_entry = ttkb.Entry(device_name_frame, textvariable=self.device_name_var, width=20)
        device_name_entry.pack(side=LEFT, padx=5, fill=X, expand=YES)

        # 硬件版本部分
        hw_version_frame = ttkb.Frame(device_hw_frame)
        hw_version_frame.pack(side=LEFT, fill=X, expand=YES)
        ttkb.Label(hw_version_frame, text="硬件版本:").pack(side=LEFT, padx=5)
        self.main_version_var = ttkb.StringVar(value="1")
        self.sub_version_var = ttkb.StringVar(value="0")
        self.patch_version_var = ttkb.StringVar(value="0")

        main_version_entry = ttkb.Entry(hw_version_frame, textvariable=self.main_version_var, width=5)
        sub_version_entry = ttkb.Entry(hw_version_frame, textvariable=self.sub_version_var, width=5)
        patch_version_entry = ttkb.Entry(hw_version_frame, textvariable=self.patch_version_var, width=5)

        main_version_entry.pack(side=LEFT, padx=1)
        ttkb.Label(hw_version_frame, text=".").pack(side=LEFT)
        sub_version_entry.pack(side=LEFT, padx=1)
        ttkb.Label(hw_version_frame, text=".").pack(side=LEFT)
        patch_version_entry.pack(side=LEFT, padx=1)

        # 设备代码和作者（同一行）
        code_author_frame = ttkb.Frame(device_info_frame)
        code_author_frame.pack(fill=X, pady=5)

        # 设备代码部分
        device_code_frame = ttkb.Frame(code_author_frame)
        device_code_frame.pack(side=LEFT, fill=X, expand=YES, padx=(0, 10))
        ttkb.Label(device_code_frame, text="设备代码:").pack(side=LEFT, padx=5)
        self.device_code_var = ttkb.StringVar(value="")
        device_code_entry = ttkb.Entry(device_code_frame, textvariable=self.device_code_var, width=16)
        device_code_entry.pack(side=LEFT, padx=5, fill=X, expand=YES)

        # 作者信息部分
        author_frame = ttkb.Frame(code_author_frame)
        author_frame.pack(side=LEFT, fill=X, expand=YES)
        ttkb.Label(author_frame, text="作者:").pack(side=LEFT, padx=5)
        self.author_var = ttkb.StringVar(value="latefly")
        author_entry = ttkb.Entry(author_frame, textvariable=self.author_var, width=32)
        author_entry.pack(side=LEFT, padx=5, fill=X, expand=YES)

        # 固件信息显示区域
        info_frame = ttkb.LabelFrame(left_frame, text="固件信息", padding=10)
        info_frame.pack(fill=BOTH, expand=YES, pady=5)
        self.info_text = scrolledtext.ScrolledText(info_frame, wrap=tk.WORD, height=8)
        self.info_text.pack(fill=BOTH, expand=YES)
        self.info_text.config(state=DISABLED)

        # 右侧：日志区域（占1/3宽度）
        right_frame = ttkb.Frame(content_frame)
        right_frame.pack(side=RIGHT, fill=BOTH, expand=NO, padx=5)
        right_frame.configure(width=450)  # 固定宽度

        # 日志显示区域
        log_frame = ttkb.LabelFrame(right_frame, text="操作日志", padding=10)
        log_frame.pack(fill=BOTH, expand=YES, pady=5)

        clear_log_btn = ttkb.Button(log_frame, text="清空日志", command=self.clear_log, bootstyle=WARNING)
        clear_log_btn.pack(side=TOP, fill=X, pady=5, padx=5)

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=35)
        self.log_text.pack(fill=BOTH, expand=YES)
        self.log_text.config(state=DISABLED)

    def add_file(self):
        """添加新文件并指定地址"""
        file_path = filedialog.askopenfilename(
            filetypes=[
                ("支持的文件", "*.hex *.bin"),
                ("HEX文件", "*.hex"),
                ("BIN文件", "*.bin"),
                ("所有文件", "*.*")
            ]
        )

        if not file_path:
            return

        # 获取文件类型
        file_ext = os.path.splitext(file_path)[1].lower()
        file_type = "hex" if file_ext == ".hex" else "bin"

        # 读取文件数据
        try:
            if file_type == "hex":
                file_data = self.hex_to_bin(file_path)
            else:  # bin
                with open(file_path, "rb") as f:
                    file_data = f.read()

            file_size = len(file_data)
            if file_size == 0:
                self.log(f"错误: 所选文件 '{os.path.basename(file_path)}' 为空")
                return

        except Exception as e:
            self.log(f"读取文件时出错: {str(e)}")
            return

        # 询问用户输入起始地址
        addr_dialog = AddressInputDialog(self, title="设置起始地址")
        self.wait_window(addr_dialog)
        addr_str = addr_dialog.result

        if not addr_str:
            return

        # 验证地址格式
        try:
            start_addr = int(addr_str, 16)
            if start_addr < 0:
                raise ValueError("地址不能为负数")
        except ValueError as e:
            self.log(f"无效的地址格式: {str(e)}")
            return

        # 检查地址重叠
        end_addr = start_addr + file_size - 1  # 计算结束地址(包含)
        overlap, conflict = self.check_address_overlap(start_addr, end_addr)

        if overlap:
            res = messagebox.askyesno(
                "地址重叠",
                f"所选地址范围与已有文件重叠:\n"
                f"新文件: 0x{start_addr:08X} - 0x{end_addr:08X}\n"
                f"冲突文件: 0x{conflict[0]:08X} - 0x{conflict[1]:08X}\n"
                "是否仍要添加?"
            )
            if not res:
                return

        # 添加文件信息
        file_name = os.path.basename(file_path)
        status = "重叠" if overlap else "正常"

        self.file_entries.append({
            "path": file_path,
            "name": file_name,
            "type": file_type,
            "addr": start_addr,
            "size": file_size,
            "data": file_data,
            "status": status,
            "checksum": binascii.crc32(file_data) & 0xFFFFFFFF  # 计算文件CRC32
        })

        # 更新地址范围列表
        self.address_ranges.append((start_addr, end_addr))

        # 添加到表格
        self.file_tree.insert("", tk.END, values=(
            file_name,
            file_type.upper(),
            f"0x{start_addr:08X}",
            file_size,
            status
        ))

        self.log(f"添加文件: {file_name}，地址: 0x{start_addr:08X}，大小: {file_size}字节")

    def remove_file(self):
        """移除选中的文件"""
        selected_items = self.file_tree.selection()
        if not selected_items:
            self.log("请先选择要移除的文件")
            return

        # 获取选中项的索引
        indices = []
        for item in selected_items:
            idx = int(self.file_tree.index(item))
            indices.append(idx)

        # 按逆序删除，避免索引变化问题
        for idx in sorted(indices, reverse=True):
            file_info = self.file_entries.pop(idx)
            self.address_ranges.pop(idx)
            self.file_tree.delete(self.file_tree.get_children()[idx])
            self.log(f"移除文件: {file_info['name']}")

        # 更新状态
        self.check_all_overlaps()

    def clear_files(self):
        """清空文件列表"""
        if not self.file_entries:
            self.log("文件列表已为空")
            return

        if messagebox.askyesno("确认", "确定要清空所有文件吗?"):
            self.file_entries.clear()
            self.address_ranges.clear()
            for item in self.file_tree.get_children():
                self.file_tree.delete(item)
            self.log("已清空所有文件")

    def check_address_overlap(self, new_start, new_end):
        """检查新地址范围是否与已有地址重叠"""
        for i, (start, end) in enumerate(self.address_ranges):
            # 检查两个区间是否重叠
            if not (new_end < start or new_start > end):
                return True, (start, end)
        return False, None

    def check_all_overlaps(self):
        """检查所有文件的地址重叠情况"""
        if len(self.file_entries) < 2:
            # 更新状态为正常
            for entry in self.file_entries:
                entry["status"] = "正常"
            self.update_file_statuses()
            return

        # 先将所有状态设为正常
        for entry in self.file_entries:
            entry["status"] = "正常"

        overlaps_found = False
        # 比较所有文件对
        for i in range(len(self.address_ranges)):
            start1, end1 = self.address_ranges[i]
            for j in range(i + 1, len(self.address_ranges)):
                start2, end2 = self.address_ranges[j]

                if not (end1 < start2 or start1 > end2):
                    overlaps_found = True
                    self.file_entries[i]["status"] = "重叠"
                    self.file_entries[j]["status"] = "重叠"
                    self.log(f"发现地址重叠: "
                             f"文件{i + 1} (0x{start1:08X}-0x{end1:08X}) 与 "
                             f"文件{j + 1} (0x{start2:08X}-0x{end2:08X})")

        # 更新表格状态
        self.update_file_statuses()

        if not overlaps_found:
            self.log("所有文件地址范围无重叠")

    def update_file_statuses(self):
        """更新表格中的文件状态"""
        for i, item in enumerate(self.file_tree.get_children()):
            values = list(self.file_tree.item(item, "values"))
            values[4] = self.file_entries[i]["status"]
            self.file_tree.item(item, values=values)

    def log(self, message):
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"
        self.log_messages.append(log_entry)

        self.log_text.config(state=NORMAL)
        self.log_text.insert(END, log_entry)
        self.log_text.see(END)
        self.log_text.config(state=DISABLED)

    def clear_log(self):
        self.log_messages = []
        self.log_text.config(state=NORMAL)
        self.log_text.delete(1.0, END)
        self.log_text.config(state=DISABLED)
        self.log("日志已清空")

    def generate_abin(self):
        if not self.file_entries:
            self.log("错误: 请先添加至少一个文件")
            return

        # 检查是否有地址重叠
        has_overlaps = any(entry["status"] == "重叠" for entry in self.file_entries)
        if has_overlaps:
            res = messagebox.askyesno(
                "地址重叠",
                "检测到文件地址重叠，继续生成可能导致固件错误。\n是否继续?"
            )
            if not res:
                return

        try:
            # 获取输入参数
            author = self.author_var.get()[:32]  # 限制最长32字节
            if not author:
                author = "latefly"  # 默认值

            # 检查版本号
            try:
                main_version = int(self.main_version_var.get())
                sub_version = int(self.sub_version_var.get())
                patch_version = int(self.patch_version_var.get())
            except ValueError:
                self.log("错误: 版本号必须为整数")
                return

            device_name = self.device_name_var.get()[:32]  # 限制最长32字节
            device_code = self.device_code_var.get()[:16]  # 限制最长16字节

            # 按地址排序文件
            sorted_files = sorted(self.file_entries, key=lambda x: x["addr"])

            # 生成TLV格式固件
            abin_data = self.generate_tlv_firmware(
                author, main_version, sub_version, patch_version,
                device_name, device_code, sorted_files
            )

            # 保存ABIN文件
            save_path = filedialog.asksaveasfilename(
                defaultextension=".abin",
                filetypes=[("ABIN文件", "*.abin"), ("所有文件", "*.*")]
            )

            if save_path:
                with open(save_path, "wb") as f:
                    f.write(abin_data)
                self.log(f"ABIN固件生成成功并保存至: {save_path}")

                # 显示固件信息
                self.update_firmware_info(sorted_files)

        except Exception as e:
            self.log(f"生成ABIN固件时发生错误: {str(e)}")

    def update_firmware_info(self, sorted_files):
        """更新固件信息显示"""
        self.info_text.config(state=NORMAL)
        self.info_text.delete(1.0, END)

        self.info_text.insert(END, "固件汇总信息:\n\n")
        self.info_text.insert(END, f"文件总数: {len(sorted_files)}\n")

        total_size = sum(file["size"] for file in sorted_files)
        self.info_text.insert(END, f"总大小: {total_size}字节\n\n")

        self.info_text.insert(END, "文件列表:\n")
        for i, file in enumerate(sorted_files, 1):
            self.info_text.insert(END,
                                  f"  {i}. {file['name']} - 地址: 0x{file['addr']:08X} - 0x{file['addr'] + file['size'] - 1:08X}, "
                                  f"大小: {file['size']}字节, 状态: {file['status']}\n"
                                  )

        self.info_text.config(state=DISABLED)

    def hex_to_bin(self, hex_path):
        """将HEX文件转换为BIN数据"""
        bin_data = bytearray()

        with open(hex_path, "r") as f:
            for line in f:
                line = line.strip()
                if not line or line[0] != ':':
                    continue

                # 解析HEX记录
                try:
                    byte_count = int(line[1:3], 16)
                    address = int(line[3:7], 16)
                    record_type = int(line[7:9], 16)

                    # 数据记录
                    if record_type == 0:
                        data = bytes.fromhex(line[9:9 + byte_count * 2])
                        bin_data.extend(data)
                    # 忽略其他类型记录
                except Exception as e:
                    self.log(f"解析HEX文件错误: {str(e)}")
                    raise

        return bin_data

    def generate_tlv_firmware(self, author, main_version, sub_version, patch_version,
                              device_name, device_code, files):
        """生成TLV格式的固件数据，包含多个文件信息"""
        try:
            # 获取当前时间戳
            epoch = datetime.datetime(1970, 1, 1)
            now = datetime.datetime.now()
            timestamp = int((now - epoch).total_seconds())
            timestamp += 62167219200  # 公元0年到1970年的秒数偏移

            # 构建内部内容
            inner_content = bytearray()

            # 作者TLV
            author_bytes = author.encode('ascii')
            inner_content.extend(self.create_tlv(self.TLV_AUTHOR, author_bytes))

            # 时间戳TLV (8字节)
            timestamp_bytes = struct.pack('>Q', timestamp)
            inner_content.extend(self.create_tlv(self.TLV_TIMESTAMP, timestamp_bytes))

            # 硬件版本TLV (6字节: 主版本2字节 + 次版本2字节 + 微更码2字节)
            version_bytes = struct.pack('>HHH', main_version, sub_version, patch_version)
            inner_content.extend(self.create_tlv(self.TLV_HW_VERSION, version_bytes))

            # 设备名称TLV
            device_name_bytes = device_name.encode('ascii')
            inner_content.extend(self.create_tlv(self.TLV_DEVICE_NAME, device_name_bytes))

            # 设备代码TLV
            device_code_bytes = device_code.encode('ascii')
            inner_content.extend(self.create_tlv(self.TLV_DEVICE_CODE, device_code_bytes))

            # 文件总数TLV
            inner_content.extend(self.create_tlv(self.TLV_FILE_COUNT, struct.pack('>I', len(files))))

            # 逐个添加文件信息和数据
            for file_idx, file in enumerate(files):
                self.log(f"处理文件 {file_idx + 1}/{len(files)}: {file['name']}")

                # 文件信息TLV: 起始地址(8字节) + 大小(4字节) + 文件名(以null结尾)
                file_info = struct.pack('>QI', file["addr"], file["size"])
                file_info += file["name"].encode('ascii') + b'\x00'  # 文件名以null结尾
                inner_content.extend(self.create_tlv(self.TLV_FILE_INFO, file_info))

                # 文件校验和TLV
                inner_content.extend(self.create_tlv(self.TLV_FILE_CHECKSUM, struct.pack('>I', file["checksum"])))

                # 文件数据TLV (64K每段)
                total_chunks = (file["size"] + self.CHUNK_SIZE - 1) // self.CHUNK_SIZE
                for chunk_idx in range(total_chunks):
                    start = chunk_idx * self.CHUNK_SIZE
                    end = start + self.CHUNK_SIZE
                    chunk = file["data"][start:end]

                    # 写入当前块索引 (4字节: 文件索引 << 16 | 块索引)
                    combined_index = (file_idx << 16) | chunk_idx
                    inner_content.extend(self.create_tlv(self.TLV_CHUNK_INDEX, struct.pack('>I', combined_index)))

                    # 写入块数据
                    inner_content.extend(self.create_tlv(self.TLV_FILE_DATA, chunk))

                    if chunk_idx % 10 == 0 or chunk_idx == total_chunks - 1:
                        self.log(f"  处理数据块 {chunk_idx + 1}/{total_chunks}")

            # 计算内部内容长度和CRC32
            inner_length = len(inner_content)
            inner_length_bytes = struct.pack('>I', inner_length)  # 4字节长度

            # 内部包头 (0x000000FF) + 长度 + 内容
            inner_package = bytearray()
            inner_package.extend(struct.pack('>I', 0x000000FF))  # 内部固定包头 (4字节)
            inner_package.extend(inner_length_bytes)
            inner_package.extend(inner_content)

            # 计算CRC32
            crc = binascii.crc32(inner_package) & 0xFFFFFFFF  # 确保为32位无符号整数
            crc_bytes = struct.pack('>I', crc)  # 4字节CRC

            # 外部包头 (0x00000002) + 总长度 + 内部包 + CRC
            outer_length = len(inner_package) + 4  # 加上4字节CRC的长度
            outer_length_bytes = struct.pack('>I', outer_length)  # 4字节长度

            outer_package = bytearray()
            outer_package.extend(struct.pack('>I', 0x00000002))  # 外部固定包头 (4字节)
            outer_package.extend(outer_length_bytes)
            outer_package.extend(inner_package)
            outer_package.extend(crc_bytes)

            return outer_package
        except Exception as e:
            self.log(f"生成TLV结构时出错: {str(e)}")
            raise

    def create_tlv(self, t, v):
        """创建TLV结构，使用4字节Type和4字节Length"""
        t_bytes = struct.pack('>I', t)  # 4字节type，大端模式
        l_bytes = struct.pack('>I', len(v))  # 4字节length，大端模式
        return t_bytes + l_bytes + v

    def read_abin(self):
        """读取ABIN文件并解析多文件信息"""
        file_path = filedialog.askopenfilename(
            filetypes=[("ABIN文件", "*.abin"), ("所有文件", "*.*")]
        )
        if not file_path:
            return

        try:
            with open(file_path, "rb") as f:
                abin_data = f.read()

            # 解析ABIN文件
            info = self.parse_abin(abin_data)

            # 显示解析结果
            self.info_text.config(state=NORMAL)
            self.info_text.delete(1.0, END)

            if info["valid"]:
                # 清空当前文件列表
                self.clear_files(confirm=False)

                # 添加解析出的文件
                for file_info in info["files"]:
                    self.file_entries.append(file_info)
                    start_addr = file_info["addr"]
                    end_addr = start_addr + file_info["size"] - 1
                    self.address_ranges.append((start_addr, end_addr))

                    self.file_tree.insert("", tk.END, values=(
                        file_info["name"],
                        file_info["type"].upper(),
                        f"0x{start_addr:08X}",
                        file_info["size"],
                        "正常"
                    ))

                # 检查地址重叠
                self.check_all_overlaps()

                # 显示信息
                self.info_text.insert(END, "ABIN文件解析成功:\n\n")
                self.info_text.insert(END, f"作者: {info['author']}\n")
                self.info_text.insert(END, f"生成时间: {info['timestamp']}\n")
                self.info_text.insert(END, f"硬件版本: {info['hw_version']}\n")
                self.info_text.insert(END, f"设备名称: {info['device_name']}\n")
                self.info_text.insert(END, f"设备代码: {info['device_code']}\n")
                self.info_text.insert(END, f"文件总数: {len(info['files'])}\n")

                # 更新输入框
                self.author_var.set(info['author'])
                main_v, sub_v, patch_v = info['hw_version'].split('.')
                self.main_version_var.set(main_v)
                self.sub_version_var.set(sub_v)
                self.patch_version_var.set(patch_v)
                self.device_name_var.set(info['device_name'])
                self.device_code_var.set(info['device_code'])

                self.log(f"ABIN文件解析成功，包含 {len(info['files'])} 个文件")
            else:
                self.info_text.insert(END, f"ABIN文件解析失败: {info['error']}\n")
                self.log(f"ABIN文件解析失败: {info['error']}")

            self.info_text.config(state=DISABLED)

        except Exception as e:
            self.log(f"读取ABIN文件时发生错误: {str(e)}")

    def clear_files(self, confirm=True):
        """清空文件列表，confirm参数控制是否需要确认"""
        if not self.file_entries:
            self.log("文件列表已为空")
            return

        if not confirm or messagebox.askyesno("确认", "确定要清空所有文件吗?"):
            self.file_entries.clear()
            self.address_ranges.clear()
            for item in self.file_tree.get_children():
                self.file_tree.delete(item)
            self.log("已清空所有文件")

    def parse_abin(self, data):
        """解析ABIN文件并返回信息，支持多文件格式"""
        result = {
            "valid": False,
            "author": "",
            "timestamp": "",
            "hw_version": "",
            "device_name": "",
            "device_code": "",
            "files": [],  # 解析出的文件列表
            "error": ""
        }

        try:
            # 检查外部包头 (4字节)
            if len(data) < 8:  # 4字节包头 + 4字节长度
                result["error"] = "文件太短，不符合ABIN格式"
                return result

            outer_header = struct.unpack('>I', data[0:4])[0]
            if outer_header != 0x00000002:
                result["error"] = f"外部包头不正确，应为0x00000002，实际为0x{outer_header:08X}"
                return result

            outer_length = struct.unpack('>I', data[4:8])[0]

            # 检查总长度
            if len(data) < 8 + outer_length:  # 8字节头部 + 内容长度
                result["error"] = "文件长度与头部信息不符"
                return result

            # 提取内部包和CRC (4字节)
            inner_package = data[8:8 + outer_length - 4]  # 减去4字节CRC
            crc_received = struct.unpack('>I', data[8 + outer_length - 4:8 + outer_length])[0]

            # 计算CRC32并验证
            crc_calculated = binascii.crc32(inner_package) & 0xFFFFFFFF
            if crc_received != crc_calculated:
                result["error"] = f"CRC校验失败，接收:0x{crc_received:08X}, 计算:0x{crc_calculated:08X}"
                return result

            # 解析内部包
            if len(inner_package) < 8:  # 4字节包头 + 4字节长度
                result["error"] = "内部包太短"
                return result

            inner_header = struct.unpack('>I', inner_package[0:4])[0]
            if inner_header != 0x000000FF:
                result["error"] = f"内部包头不正确，应为0x000000FF，实际为0x{inner_header:08X}"
                return result

            inner_length = struct.unpack('>I', inner_package[4:8])[0]
            inner_content = inner_package[8:8 + inner_length]

            # 解析TLV内容 (4字节Type + 4字节Length + Value)
            tlv_list = self.parse_tlv_list(inner_content)

            # 提取文件总数
            file_count = 0
            for tlv in tlv_list:
                if tlv['type'] == self.TLV_FILE_COUNT:
                    file_count = struct.unpack('>I', tlv['value'])[0]
                    break

            # 解析文件信息和数据
            files = []
            current_file = None
            file_data_chunks = {}  # {file_idx: {chunk_idx: data, ...}, ...}

            for tlv in tlv_list:
                if tlv['type'] == self.TLV_AUTHOR:
                    result["author"] = tlv['value'].decode('ascii', errors='replace')

                elif tlv['type'] == self.TLV_TIMESTAMP and len(tlv['value']) == 8:
                    timestamp = struct.unpack('>Q', tlv['value'])[0]
                    epoch_timestamp = timestamp - 62167219200
                    try:
                        dt = datetime.datetime.fromtimestamp(epoch_timestamp)
                        result["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
                    except:
                        result["timestamp"] = f"未知 ({timestamp})"

                elif tlv['type'] == self.TLV_HW_VERSION and len(tlv['value']) == 6:
                    main_v, sub_v, patch_v = struct.unpack('>HHH', tlv['value'])
                    result["hw_version"] = f"{main_v}.{sub_v}.{patch_v}"

                elif tlv['type'] == self.TLV_DEVICE_NAME:
                    result["device_name"] = tlv['value'].decode('ascii', errors='replace')

                elif tlv['type'] == self.TLV_DEVICE_CODE:
                    result["device_code"] = tlv['value'].decode('ascii', errors='replace')

                elif tlv['type'] == self.TLV_FILE_INFO:
                    # 解析文件信息: 8字节地址 + 4字节大小 + 文件名
                    if len(tlv['value']) < 12:
                        self.log("警告: 无效的文件信息TLV")
                        continue

                    addr = struct.unpack('>Q', tlv['value'][0:8])[0]
                    size = struct.unpack('>I', tlv['value'][8:12])[0]
                    name_bytes = tlv['value'][12:]
                    name = name_bytes.split(b'\x00')[0].decode('ascii', errors='replace')

                    # 创建新文件对象
                    current_file = {
                        "name": name,
                        "type": "bin",  # 默认为bin，无法从ABIN中区分原始类型
                        "addr": addr,
                        "size": size,
                        "data": b"",
                        "status": "正常",
                        "checksum": 0
                    }
                    files.append(current_file)

                elif tlv['type'] == self.TLV_FILE_CHECKSUM and current_file:
                    # 记录文件校验和
                    if len(tlv['value']) == 4:
                        current_file["checksum"] = struct.unpack('>I', tlv['value'])[0]

                elif tlv['type'] == self.TLV_CHUNK_INDEX:
                    # 解析块索引: 文件索引 << 16 | 块索引
                    if len(tlv['value']) != 4:
                        self.log("警告: 无效的块索引TLV")
                        continue

                    combined_index = struct.unpack('>I', tlv['value'])[0]
                    file_idx = (combined_index >> 16) & 0xFFFF
                    chunk_idx = combined_index & 0xFFFF

                    # 为下一个数据块准备存储位置
                    if file_idx not in file_data_chunks:
                        file_data_chunks[file_idx] = {}
                    current_chunk_key = (file_idx, chunk_idx)

                elif tlv['type'] == self.TLV_FILE_DATA and current_chunk_key:
                    # 存储文件数据块
                    file_idx, chunk_idx = current_chunk_key
                    if file_idx < len(files):  # 确保文件索引有效
                        file_data_chunks[file_idx][chunk_idx] = tlv['value']
                    current_chunk_key = None  # 重置

            # 重组文件数据
            for file_idx in range(len(files)):
                if file_idx in file_data_chunks:
                    chunks = file_data_chunks[file_idx]
                    # 按块索引排序并合并
                    sorted_chunks = sorted(chunks.items(), key=lambda x: x[0])
                    file_data = bytearray()
                    for _, chunk_data in sorted_chunks:
                        file_data.extend(chunk_data)
                    # 截断到正确大小
                    if len(file_data) > files[file_idx]["size"]:
                        file_data = file_data[:files[file_idx]["size"]]
                    files[file_idx]["data"] = bytes(file_data)

                    # 验证校验和
                    calculated_crc = binascii.crc32(files[file_idx]["data"]) & 0xFFFFFFFF
                    if calculated_crc != files[file_idx]["checksum"]:
                        self.log(f"警告: 文件 '{files[file_idx]['name']}' 校验和不匹配")
                        files[file_idx]["status"] = "校验和错误"

            result["files"] = files
            result["valid"] = True

        except Exception as e:
            result["error"] = str(e)

        return result

    def parse_tlv_list(self, data):
        """解析TLV数据并返回TLV字典列表，支持4字节Type和4字节Length"""
        tlv_list = []
        offset = 0

        while offset + 8 <= len(data):  # 4字节Type + 4字节Length = 8字节
            # 解析Type (4字节)
            t = struct.unpack('>I', data[offset:offset + 4])[0]
            offset += 4

            # 解析Length (4字节)
            l = struct.unpack('>I', data[offset:offset + 4])[0]
            offset += 4

            # 解析Value
            if offset + l > len(data):
                self.log(f"警告: TLV数据不完整，类型:0x{t:08X}, 预期长度:{l}, 实际剩余:{len(data) - offset}")
                break

            v = data[offset:offset + l]
            offset += l

            tlv_list.append({'type': t, 'length': l, 'value': v})

        return tlv_list

    def export_hex(self):
        if not self.file_entries:
            self.log("错误: 请先添加或加载文件")
            return

        try:
            # 按地址排序文件
            sorted_files = sorted(self.file_entries, key=lambda x: x["addr"])

            # 保存为HEX文件
            save_path = filedialog.asksaveasfilename(
                defaultextension=".hex",
                filetypes=[("HEX文件", "*.hex"), ("所有文件", "*.*")]
            )

            if save_path:
                # 生成合并的HEX数据
                hex_lines = []

                for file in sorted_files:
                    # 获取起始地址
                    start_addr = file["addr"]
                    data = file["data"]
                    offset = 0
                    line_length = 16  # 每行16字节

                    while offset < len(data):
                        chunk = data[offset:offset + line_length]
                        chunk_len = len(chunk)

                        # 地址处理
                        addr = start_addr + offset

                        # 数据记录: :LLAAAATT[DD...DD]CC
                        line = f":{chunk_len:02X}{addr & 0xFFFF:04X}00"
                        line += chunk.hex().upper()

                        # 计算校验和
                        checksum = 0
                        bytes_list = [chunk_len, (addr >> 8) & 0xFF, addr & 0xFF, 0x00] + list(chunk)
                        for b in bytes_list:
                            checksum += b
                        checksum = (-checksum) & 0xFF
                        line += f"{checksum:02X}"

                        hex_lines.append(line)
                        offset += chunk_len

                    self.log(f"已处理 {file['name']} 的HEX转换")

                # 添加扩展线性地址记录（如果需要）
                max_addr = max(file["addr"] + file["size"] for file in sorted_files)
                if max_addr >= 0x10000:
                    # 只需要添加一次最高地址的扩展记录
                    extended_addr = (max_addr >> 16) & 0xFFFF
                    line = f":02000004{extended_addr:04X}"
                    # 计算校验和
                    checksum = (0x02 + 0x04 + (extended_addr >> 8) + (extended_addr & 0xFF)) & 0xFF
                    checksum = (-checksum) & 0xFF
                    line += f"{checksum:02X}"
                    hex_lines.insert(0, line)

                # 添加结束记录
                hex_lines.append(":00000001FF")

                # 保存文件
                with open(save_path, "w") as f:
                    f.write("\n".join(hex_lines))

                self.log(f"成功导出合并的HEX文件: {save_path}")

        except Exception as e:
            self.log(f"导出HEX文件时发生错误: {str(e)}")

    def export_bin(self):
        if not self.file_entries:
            self.log("错误: 请先添加或加载文件")
            return

        try:
            # 询问用户是否要合并导出或单独导出
            res = messagebox.askyesnocancel(
                "导出选项",
                "请选择导出方式:\n"
                "是 - 合并所有文件为一个BIN\n"
                "否 - 每个文件单独导出为BIN\n"
                "取消 - 取消导出"
            )

            if res is None:  # 取消
                return

            # 按地址排序文件
            sorted_files = sorted(self.file_entries, key=lambda x: x["addr"])

            if res:  # 合并导出
                save_path = filedialog.asksaveasfilename(
                    defaultextension=".bin",
                    filetypes=[("BIN文件", "*.bin"), ("所有文件", "*.*")]
                )

                if save_path:
                    # 计算总大小和地址范围
                    if not sorted_files:
                        self.log("错误: 没有文件可导出")
                        return

                    min_addr = sorted_files[0]["addr"]
                    max_addr = max(file["addr"] + file["size"] for file in sorted_files)
                    total_size = max_addr - min_addr

                    # 创建空白缓冲区
                    bin_data = bytearray(total_size)

                    # 填充各个文件的数据
                    for file in sorted_files:
                        start = file["addr"] - min_addr
                        end = start + file["size"]
                        if end > len(bin_data):
                            # 扩展缓冲区（如果需要）
                            bin_data.extend(b'\x00' * (end - len(bin_data)))
                        bin_data[start:end] = file["data"]

                    # 保存文件
                    with open(save_path, "wb") as f:
                        f.write(bin_data)

                    self.log(f"成功导出合并的BIN文件: {save_path}，大小: {len(bin_data)}字节")
                    self.log(f"地址范围: 0x{min_addr:08X} - 0x{max_addr - 1:08X}")

            else:  # 单独导出
                # 询问保存目录
                export_dir = filedialog.askdirectory(title="选择保存目录")
                if not export_dir:
                    return

                # 逐个导出文件
                for i, file in enumerate(sorted_files, 1):
                    # 生成文件名
                    base_name = os.path.splitext(file["name"])[0]
                    file_name = f"{base_name}_0x{file['addr']:08X}.bin"
                    file_path = os.path.join(export_dir, file_name)

                    with open(file_path, "wb") as f:
                        f.write(file["data"])

                    self.log(f"导出文件 {i}/{len(sorted_files)}: {file_path}")

                self.log(f"所有文件已导出至: {export_dir}")

        except Exception as e:
            self.log(f"导出BIN文件时发生错误: {str(e)}")

    def create_widgets(self):
        pass
